package com.hzw.saas.web.app.hpc.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.hzw.saas.api.hpc.IHpcAccountDetailService;
import com.hzw.saas.api.hpc.IHpcAccountIncreaseService;
import com.hzw.saas.api.hpc.IHpcAccountRecordService;
import com.hzw.saas.api.hpc.IHpcAccountUserService;
import com.hzw.saas.api.hpc.bo.HpcAccountDetailBo;
import com.hzw.saas.api.hpc.bo.HpcAccountInfoBo;
import com.hzw.saas.api.hpc.bo.HpcAccountUserBo;
import com.hzw.saas.api.hpc.enums.HpcAccountUserType;
import com.hzw.saas.api.property.IPropertyService;
import com.hzw.saas.api.property.enmus.SysDictKeyEnum;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.aspect.RedisLockAspect;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.RedisUtil;
import com.hzw.saas.common.security.pojo.dto.SaasUser;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.Arith;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.web.app.hpc.param.HpcAccountDetailParam;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 超算中心控制器
 * </p>
 *
 * @author sonam
 * @since 2021/1/26 13:44
 */
@RestController
@RequestMapping
@Api(tags = "机时中心/账号管理")
@RequiredArgsConstructor
public class HpcController {

    private final RedissonClient redissonClient;
    private final IHpcAccountUserService accountUserService;
    private final IHpcAccountIncreaseService increaseService;
    private final IHpcAccountRecordService hpcAccountRecordService;
    private final IHpcAccountDetailService hpcAccountDetailService;

    @Resource(name = IPropertyService.SYS_DICT_BEAN)
    private final IPropertyService<SysDictKeyEnum> propertyService;

    @ApiOperationSort(1)
    @GetMapping("/hpcs/accounts")
    @SysLog(operation = "获取所有超算中心的所有用户账户")
    @ApiOperation(value = "获取所有超算中心的所有用户账户", notes = "获取所有超算中心的所有用户账户")
    public ResponseEntity<List<HpcAccountInfoBo>> listHpcWithAccounts(@RequestParam(defaultValue = "false") @ApiParam("是否过期，true｜false，默认查未过期") Boolean isExpire,
                                                                      @RequestParam(required = false) @ApiParam("是否为主账户，true｜false，默认查全部") Boolean isMain) {
        SaasUser user = SecurityUtils.getUser();
        IPage<HpcAccountUserBo> usableListByUserIdPage = accountUserService.getUsableListByUserIdPage(PageParam.getBigPageParam(),
            user.getUserId(), isMain == null ? null : isMain ? HpcAccountUserType.MAIN.value() : HpcAccountUserType.SUB.value(), isExpire);
        if (usableListByUserIdPage == null || CollectionUtils.isEmpty(usableListByUserIdPage.getRecords())) {
            return ResponseEntity.ok(new ArrayList<>());
        }
        List<HpcAccountUserBo> accountUserBos = usableListByUserIdPage.getRecords();
        List<HpcAccountInfoBo> hpcAccountList = new ArrayList<>();
        accountUserBos.forEach(accountUserBo -> {
            double sumIncrease = increaseService.getSumIncreaseByStartDate(Collections.singletonList(accountUserBo.getHpcAccountRecordId()), new Date());
            HpcAccountInfoBo accountInfoBo = MapperUtil.nf().map(accountUserBo, HpcAccountInfoBo.class);
            accountInfoBo.setAccountId(accountUserBo.getPid());
            accountInfoBo.setExpireDate(accountUserBo.getExpireTime());
            accountInfoBo.setRegisterDate(accountUserBo.getCreateTime());
            accountInfoBo.setSubExpireDate(accountUserBo.getSubExpireTime());
            accountInfoBo.setHpcAccount(accountUserBo.getHpcAccountSnapshot());
            accountInfoBo.setHpcAmount(NumberUtil.round(Arith.add(accountUserBo.getCoreTimeBalanceTotal(), sumIncrease), 2).doubleValue());
            accountInfoBo.setHpcBalance(NumberUtil.round(Arith.add(accountUserBo.getCoreTimeBalance(), sumIncrease), 2).doubleValue());
            hpcAccountList.add(accountInfoBo);
        });

        return ResponseEntity.ok(hpcAccountList);
    }

    @Deprecated
    @GetMapping("/hpcs/accounts/balance")
    @SysLog
    @ApiOperation(value = "获取所有超算中心用户账户的机时余额总和", notes = "获取所有超算中心用户账户的机时余额总和")
    @ApiResponse(code = 200, message = "机时余额总和", response = Long.class)
    @ApiOperationSort(2)
    public ResponseEntity<Double> sumHpcAccountBalance() {
        SaasUser user = SecurityUtils.getUser();
        double sumBalance = accountUserService.sumHpcAccountBalance(user.getUserId());
        return ResponseEntity.ok(NumberUtil.round(sumBalance, 2).doubleValue());
    }

    @GetMapping("/hpc/account/detail")
    @SysLog
    @ApiOperation(value = "获取超算帐号机时明细", notes = "获取超算帐号机时统计")
    @ApiOperationSort(3)
    public ResponseEntity<IPage<HpcAccountDetailBo>> listHpcAccountDetail(PageParam pageParam, HpcAccountDetailParam param) {
        SaasUser user = SecurityUtils.getUser();
        HpcAccountUserBo accountUserBo = accountUserService.getWithRecord(param.getAccountUserId());
        if (accountUserBo == null || !Objects.equals(user.getUserId(), accountUserBo.getUserId())) {
            throw DefaultSaasException.build("无此帐号或无此帐号的访问权限").status(HttpStatus.BAD_REQUEST);
        }

        if (accountUserBo.getIsMain() == 0) {
            // 获取共享帐号的startTime
            if (DateUtil.compare(param.getStartDate(), accountUserBo.getCreateTime()) < 0) {
                param.setStartDate(DateUtil.date());
            }

            if (DateUtil.compare(param.getEndDate(), accountUserBo.getSubExpireTime()) > 0) {
                param.setEndDate(accountUserBo.getSubExpireTime());
            }
        }

        IPage<HpcAccountDetailBo> hpcAccountDetailDtoIPage = hpcAccountDetailService.listHpcAccountDetail(pageParam, Collections.singletonList(accountUserBo.getHpcAccountRecordId()), param.getStartDate(), param.getEndDate());
        return ResponseEntity.ok(hpcAccountDetailDtoIPage);
    }

    @GetMapping("/hpc/account/detail/{hpcAccountUserId}/count")
    @SysLog
    @ApiOperation(value = "指定账户统计使用核时数量", notes = "指定账户统计使用核时数量")
    @ApiResponse(code = 200, message = "账户使用核时数量", response = Integer.class)
    @ApiOperationSort(4)
    public ResponseEntity<Double> countHpcAccountDetail(@PathVariable @ApiParam("HPC账号") String hpcAccountUserId) {
        SaasUser user = SecurityUtils.getUser();
        double coreTimeSum = hpcAccountDetailService.countHpcAccountDetailByUser(user.getUserId(), hpcAccountUserId);

        BigDecimal b = new BigDecimal(coreTimeSum);
        coreTimeSum = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        return ResponseEntity.ok(coreTimeSum);
    }

    @PostMapping("/hpc/account/detail/{accountUserId}/refresh")
    @SysLog(operation = "主动刷新HPC使用明细", mode = 3)
    @ApiOperation(value = "主动刷新HPC使用明细", notes = "主动刷新HPC使用明细")
    @ApiResponses(
        {
            @ApiResponse(code = 200, message = "刷新成功"),
            @ApiResponse(code = 202, message = "正在刷新HPC明细")
        })
    @ApiOperationSort(5)
    public ResponseEntity<Void> refreshHpcDetails(@PathVariable String accountUserId) {
        HpcAccountUserBo accountUserBo = accountUserService.get(accountUserId);
        AssertUtil.assertThrow("刷新失败,该HPC用户不存在", Objects.isNull(accountUserBo));

        // 判断是否正在刷新HPC明细
        checkHpcRefresh(accountUserBo.getHpcAccountRecordId());

        // 判断用户之前是否刷新过HPC明细，并作出限制
        String key = "HPC:INTERVAL:" + accountUserId;
        long expire = RedisUtil.getExpire(key);
        AssertUtil.assertThrow("刷新间隔不能小于3分钟", expire > 0);

        // 取默认的间隔时间
        String propertyValue = propertyService.getPropertyValue(SysDictKeyEnum.INTERVAL_TIME);
        long intervalTime;
        try {
            intervalTime = Long.parseLong(propertyValue);
        } catch (RuntimeException ignored) {
            intervalTime = 180L;
        }
        RedisUtil.set(key, DateUtil.now(), intervalTime);

        // 刷新HPC账户
        hpcAccountRecordService.refreshHpcAccount(accountUserBo.getHpcAccountRecordId());
        return ResponseEntity.ok().build();
    }

    @GetMapping("/hpc/account/detail/check/refresh/{accountUserId}")
    @ApiOperation(value = "判断当前HPC帐号是否正在属性机时明细", notes = "判断当前HPC帐号是否正在属性机时明细")
    @ApiResponses(
        {
            @ApiResponse(code = 200, message = "成功"),
            @ApiResponse(code = 202, message = "正在刷新HPC明细")
        })
    @ApiOperationSort(6)
    public ResponseEntity<Void> checkRefreshHpcDetails(@PathVariable String accountUserId) {
        HpcAccountUserBo accountUserBo = accountUserService.get(accountUserId);
        if (Objects.isNull(accountUserBo)) {
            return ResponseEntity.ok().build();
        }
        // 判断是否正在刷新HPC明细
        checkHpcRefresh(accountUserBo.getHpcAccountRecordId());
        return ResponseEntity.ok().build();
    }

    /**
     * 检查是否正在刷新HPC明细
     */
    private void checkHpcRefresh(String hpcAccountRecordId) {
        RLock lock = redissonClient.getLock(RedisLockAspect.REDISSON_LOCK_PREFIX + ":FetchHpcDetails:" + hpcAccountRecordId);
        AssertUtil.assertThrow("正在刷新HPC明细", HttpStatus.ACCEPTED, lock.isLocked());
    }

}
